import memorytrace.constantes

/**
 * IHM de l'application, pas de MVP, rien de tout ça malheureusement
 */
class ConsoleUserInterface(btfile:String,eventsFile:String) {
  
/**
 * on affiche le header
 * */  
showHeader()

/**
 * composant qui parse les fichiers et qui charge les objets en memoire
 * */  
val coreParser= new MemoryTraceCore(btfile,eventsFile)   



/**
 * composant servant à requeter les allocations
 * */
val coreRequest= new memorytrace.requestmanagers.RequestAllocEvents(coreParser.buildAllocEvents())




/**
 * composant servant à requeter les backtraces
 * */
val coreRequestBt= new memorytrace.requestmanagers.RequestBackTraces(coreParser.buildStackTraces(),coreParser.buildAllocEvents())




/**
 * composant servant à requeter les backtraces
 * */
def parseFiles(){
  try{
	/*construction des objet du domaine pour les stack traces*/
		println(memorytrace.constantes.Constantes.MSG_FOUND_EVENTS +coreParser.buildAllocEvents().size)
		println(memorytrace.constantes.Constantes.MSG_FOUND_STK_TRC +coreParser.buildStackTraces().size)
		prompt()
	}catch{
	  case ex: NullPointerException          =>println(memorytrace.constantes.Constantes.MSG_FILE_NOT_FOUND)
	  case ex: java.io.FileNotFoundException =>println(memorytrace.constantes.Constantes.MSG_FILE_NOT_FOUND)
	}
}




/**
 * affiche le header du programme
 * */	  
  def showHeader()={
	/*header*/
    println("-------------------------------------------------------------------------------------------------")
    println("                                 Memory trace analyser version 1.0")
    println("System GNU Linux")
    println("Aide immediate, tapez 'help'")
    println("Author : Ameziane Nait Abdelaziz, ENSEIRB filière PRCD 2011")
    println("-------------------------------------------------------------------------------------------------")
  }
  

  
  
/**
 * affiche l'aide du programme
 * */  
  def showHelp()={
    printf("\033[34;1m help                          : affiche l'aide\033[m\n");
    printf("\033[34;1m list                          : affiche la liste des fonctions\033[m\n");
    printf("\033[34;1m listf [fonction]              : affiche la repartition des allocations de la fonction\033[m\n");
    printf("\033[34;1m min                           : affiche la plus petite allocation  \033[m\n");
    printf("\033[34;1m max                           : affiche la plus grosse allocation  \033[m\n");
    printf("\033[34;1m avg                           : affiche la moyenne des allocations \033[m\n");
    printf("\033[34;1m count                         : affiche le nombre d'evenements d'allocations \033[m\n");
    printf("\033[34;1m leaks                         : affiche le nombre de fuite mémoire \033[m\n");
    printf("\033[34;1m group by size1,size2,size3,..,sizeN\033[m\n");
    printf("\033[34;1m                               : regroupe les allocation en fonction de la distribution en arguments\033[m\n");
    printf("\033[34;1m group by range start,end      : regroupe les allocation en fonction de la distribution en arguments\033[m\n");
    
    printf("\033[34;1m clear                         : vide la boucle d'interaction\033[m\n");
    
    printf("\033[34;1m quit                          : quitte  le  programme\033[m\n");
   }
  
 
  
  
/**
 * affiche le prompt du programme
 * */
  def prompt()={
    /*prompt*/
    print("memorytrace>")
    
  }
  

  

/**
 * boucle d'interaction du programme
 * */  
  def readCommande():Int={
    
    while(true){
    	var cmd    =readLine()
    	var tokens =cmd.split(" ")
    
    	
    	tokens(0) match
    	{
    		  /*HELP COMMANDE*/
	    	  case "help" => showHelp();
	    	  				 prompt();
	    	 
	    	  /*QUIT COMMANDE*/				 
	    	  case "quit" => println("Au revoir"); return 0
	    	  
	    	  /*QUIT COMMANDE*/
	    	  case "list" => showFunctionsList()
	    	  				 prompt();
	    	  
	    	  /*QUIT COMMANDE*/
	    	  case "min"  =>showAgregate((x:Long,y:Long) => java.lang.Math.min(x,y),java.lang.Long.MAX_VALUE)
	    	    			prompt();
	    	    			
	    	  /*QUIT COMMANDE*/
	    	  case "max"  =>showAgregate((x:Long,y:Long) => java.lang.Math.max(x,y),java.lang.Long.MIN_VALUE)
	    	    			prompt();
	    	  
	    	  /*QUIT COMMANDE*/
	    	  case "sum"  =>showAgregate((x:Long,y:Long) => x+y,0)
	    	    			prompt();
	    	  
	    	  /*QUIT COMMANDE*/
	    	  case "avg"  =>showMean()
	    			  		prompt();
	    	  
	    	  /*QUIT COMMANDE*/
	    	  case "count" =>showAllocSummary()
	    			  		prompt();
	    	  
	    	  /*QUIT COMMANDE*/
	    	  case "leaks" =>showMemoryLeaks()
	    			  		prompt()
	    	  
	    	  /*QUIT COMMANDE*/
	    	  case "listf" =>
				    	  tokens=cmd.split(" ",2)
				    	  if(tokens.length==2){
							  showMemoryAllocationSummaryOfFunction(tokens(1))
				     	  }
						  prompt();
						  
			  /*QUIT COMMANDE*/			  
	    	  case "clear" => 
	    	    		  Console.flush()
	    	    		  prompt()
	    	    		  
	    	  /*QUIT COMMANDE*/  		  
	    	  case "group" =>
	    	    		 try{ 
		    	    		 tokens=cmd.split(" ",4)
		    	    		 tokens.foreach(println)
		    	    		 if(tokens.length>=3){
		    	    		    var listeV:scala.collection.mutable.ListBuffer[Long]=null
		    	    		    
		    	    		     if(tokens(2)=="range"){
		    	    		       println("RANGE")
		    	    		       if(tokens.length>3){
		    	    		         
		    	    		         val start_end =tokens(3).split(",")
		    	    		         listeV = new scala.collection.mutable.ListBuffer[Long]
		    	    		         (start_end(0).toInt to start_end(1).toInt).foreach(i=>listeV+=i)
		    	    		         
		    	    		       }
		    	    		     }
		    	    		     else{
			    	    			 val distrib=tokens(2)
			    	    			 val values =distrib.split(",")
			    	    			 listeV = new scala.collection.mutable.ListBuffer[Long]
			    	    			 values.foreach(
			    	    			     v=> 
			    	    			       {
			    	    			        
			    	    			        	 listeV+=v.toLong
			    	    			         
			    	    			         
			    	    			       }
			    	    			     )
			    	    			 
			    	    			 
			    	    		 	}
		    	    		     showMemoryAllocationDistribution(listeV)
		    	    		 	}
			    	    	}
	    	    		 catch{
	    	    			   /*Les messages doivent être dans une classes messages*/
	    			           case ex: NumberFormatException => println(memorytrace.constantes.Messages.MSG_BAD_FORMAT_NUMBER);
	    			           case ex: IndexOutOfBoundsException => println(memorytrace.constantes.Messages.MSG_BAD_DISTRIBUTION);
	    			         }	    	    		 	    	    		 
		    	    		 prompt();
	    	  case _	  =>  prompt();
	    }
    }
    0
  }
  
  
  /**
   * affiche la repartition des allocation en fonction de leur taille (comme libmemusage)
   */
  def showMemoryAllocationDistribution(l:scala.collection.mutable.ListBuffer[Long]){
	  var res=coreRequest.groupeAllocEventsByChunkSizes(l)
	  var totale = res.reduceLeft((x,y)=>x+y)
	  val histogrammes=List.map2 (l.toList,res.toList) ((x,y) =>createBarre(y*130/totale) +y.toString+"["+x.toString+" octets]" )
	  histogrammes.foreach(println)
  }
  
  /**
   * crée une barre
   */
  private def createBarre(longeur:Long)={
    var res= new StringBuilder
    (1 to longeur.toInt).foreach(t=>{res.append('=')})
    res.toString()
  }
  
  /**
   * affiche les fuites mémoire à partire du profile
   */
  def showMemoryLeaks(){
    var res=coreRequest.selectMemoryLeaks();
    //memorytrace.tools.SymboleTables.buildTables(Array{""})
  }
  
  
  /**
   * Affiche la moyenne des allocations
   */
  def showMean(){
    val res =coreRequest.aggregateEvents((x:Long,y:Long) => x+y,0)
    val summary=coreRequest.getAllocSummary()
    val mean =res/(summary._1+summary._2+summary._3)
    
    printf("%d B\n",mean)
    printf("%d KB\n",mean/1024)
    printf("%d MB\n",mean/(1024*1024))
  }
  
  /**
   * Affiche le bilan des allocations
   */
  def showAllocSummary(){
    val summary=coreRequest.getAllocSummary()
    printf("malloc  count =%d\n",summary._1)
	printf("calloc  count =%d\n",summary._2)
	printf("realloc count =%d\n",summary._3)
	printf("free    count =%d\n",summary._4)
  }
  
  /**
   * Réalise une opération d'agrégation
   */
  def showAgregate( f: (Long , Long)=> Long,initVal:Long)={
	  val res =coreRequest.aggregateEvents(f,initVal)
	  printf("%d B\n",res)
	  printf("%d KB\n",res/1024)
	  printf("%d MB\n",res/(1024*1024))
	  printf("%d GB\n",res/(1024*1024*1024))
  }
  
  
  /**
   * Affiche la liste des fonctions
   */
  def showFunctionsList()={
    printf("\033[34;1mliste des fonctions extraite du profile :\033[m\n");
    var i=0
	//ici installer un acteur qui recoit les fonctions decouvertes par l'acteur
    //lister les fonctions à partir de la commande nm plutot
    coreRequestBt.selectFunctionsFromBackTraces().foreach(t=> {i+=1; println(i+"- "+t)})
  }
  
  /**
   * Affiche un résumé des allocations
   */
  def showMemoryAllocationSummaryOfFunction(f:String)={
    val funcallocs=coreRequestBt.selectAllocEventsWhereSourceIs(f)
    printf("\033[34;1mliste des allocations faites par la fonction %s:\033[m\n",f);

    		  if(funcallocs!=null)
			  {
				  //println("Le nombre d'allocations faites par "+f+":" +0)
				  println("Le nombre de malloc     faits  par "+f+":" +funcallocs._1)
				  println("Le nombre de calloc     faits  par "+f+":" +funcallocs._2)
				  println("Le nombre de realloc    faits  par "+f+":" +funcallocs._3)
				  println("Le nombre de free       faits  par "+f+":" +funcallocs._4)
     		  }
  }
  
  /**
   * 
   */
  def startCommande()={
  }
  
  
  /**
   * 
   */
  def endCommande()={
  }
}